/* Copyright (c) 2009-2010, Sean A.O. Harney <sharney3@gmail.com> */

#include "omf.h"

/* prototypes */
EXPR_P _new_expr(EXPR_TYPE type);
EXPR_P _new_expr_leaf(void);
char *_st_leaf_val_to_str(struct st_leaf *leaf);
char *_leaf_list_to_str(LLIST_P list);

char *expr_type_to_str(EXPR_TYPE type)
{
    switch (type)
      {
      case EXPR_LEAF:
	  return "EXPR_LEAF";
      case EXPR_BINARY:
	  return "EXPR_BINARY";
      case EXPR_ASSIGNMENT:
	  return "EXPR_ASSIGNMENT";
      default:
	  return "unknown";
      }
}

//intended for printing out
//return val should be free'd
////expr_
char *leaf_val_to_str(EXPR_P expr)
{

    if (expr->type != EXPR_LEAF)	// shouldn't happen but safe to check
      {
	  error_println
	      ("leaf_val_to_str() called with non-leaf expression (%s).",
	       expr_type_to_str(expr->type));
	  return "unknown: not leaf!";
      }


    return _st_leaf_val_to_str(&(expr->u.leaf));
}

char *_st_leaf_val_to_str(struct st_leaf *leaf)
{
#define VALSTR_SZ 1024
    char *ret = checked_malloc(VALSTR_SZ);
    char *p;
    switch (leaf->type)
      {
      case LEAF_INT:
	  if (leaf->val.int_val->negative)
	    {
		ret[0] = '-';
		p = &ret[1];
	  } else
	      p = ret;

	  snprintf(p, VALSTR_SZ, "%lu", leaf->val.int_val->ulong_val);
	  break;
      case LEAF_FLOAT:
	  snprintf(ret, VALSTR_SZ, "%f", leaf->val.float_val);
	  break;
      case LEAF_STRING:
	  snprintf(ret, VALSTR_SZ, "%s", leaf->val.string_val);
	  break;
      case LEAF_BOOL:
	  snprintf(ret, VALSTR_SZ, "%s",
		   leaf->val.bool_val ? "true" : "false");
	  break;
      case LEAF_ID:
	  {
	      char *symname;
	      SYMTBL_ENT_P entry;

	      symname = leaf->val.id_val;
//all func in global symbol tbl
	      entry =
		  symbol_tbl_get_entry(glob.symbol_tbl_global, symname);
	      if (entry == NULL)
		{		//no such function exists
		    //check in local symboltbl for variable
		    entry = symbol_tbl_get_entry_variable_fatal(symname);	//noreturn attr
		}

	      if (entry->type == SYM_TYPE_VARIABLE)
		{
		    char *p;
		    switch (entry->u.variable.type)
		      {
		      case VAR_TYPE_INT:
			  if (entry->u.variable.rvalue.int_val->negative)
			    {
				ret[0] = '-';
				p = &ret[1];
			  } else
			      p = ret;

			  snprintf(p, VALSTR_SZ - 1, "%lu",
				   entry->u.variable.rvalue.int_val->
				   ulong_val);

			  break;
		      case VAR_TYPE_FLOAT:
			  snprintf(ret, VALSTR_SZ, "%f",
				   entry->u.variable.rvalue.float_val);
			  break;
		      case VAR_TYPE_STRING:
			  snprintf(ret, VALSTR_SZ, "%s",
				   entry->u.variable.rvalue.string_val);
			  break;
		      case VAR_TYPE_BOOL:
			  snprintf(ret, VALSTR_SZ, "%s",
				   entry->u.variable.rvalue.
				   bool_val ? "true" : "false");
			  break;
		      case VAR_TYPE_FUNCTION:
			  snprintf(ret, VALSTR_SZ, "%s",
				   entry->u.variable.rvalue.function_val);
			  break;
		      case VAR_TYPE_LIST:
			  free(ret);

			  ret =
			      _leaf_list_to_str(entry->u.variable.rvalue.
						list_val);
			  break;
		      case VAR_TYPE_UNINITIALIZED:
			  fatal_error_println
			      ("Reference to variable %s : uninitialized rvalue access.",
			       symname);
		      default:
			  fatal_error_println
			      ("Reference to variable %s : unknown type.",
			       symname);
		      }
	      } else if (entry == SYM_TYPE_FUNCTION)
		{

		    snprintf(ret, VALSTR_SZ, "%s", symname);	/* or equally entry->symname */
		}
	  }
	  break;
      case LEAF_LIST:
	  free(ret);
	  ret = _leaf_list_to_str(leaf->val.list_val);
	  break;
      default:
	  return "unknown";
      }
    return ret;
}

char *leaf_type_to_str(LEAF_TYPE type)
{
    switch (type)
      {
      case LEAF_INT:
	  return "INT";
      case LEAF_FLOAT:
	  return "FLOAT";
      case LEAF_STRING:
	  return "STRING";
      case LEAF_BOOL:
	  return "BOOL";
      case LEAF_ID:
	  return "ID";
      case LEAF_LIST:
	  return "LIST";
      default:
	  return "unknown";
      }
}

char *binary_op_type_to_str(BINARY_OP_TYPE type)
{
    switch (type)
      {
      case BINARY_OP_ADD:
	  return "BINARY_OP_ADD";
      case BINARY_OP_SUBTRACT:
	  return "BINARY_OP_SUBTRACT";
      case BINARY_OP_MULTIPLY:
	  return "BINARY_OP_MULTIPLY";
      case BINARY_OP_DIVIDE:
	  return "BINARY_OP_DIVIDE";
      case BINARY_OP_LT:
	  return "BINARY_OP_LT";
      case BINARY_OP_LTE:
	  return "BINARY_OP_LTE";
      case BINARY_OP_GT:
	  return "BINARY_OP_GT";
      case BINARY_OP_GTE:
	  return "BINARY_OP_GTE";
      case BINARY_OP_EQUALITY:
	  return "BINARY_OP_EQUALITY";
      case BINARY_OP_NEQUALITY:
	  return "BINARY_OP_NEQUALITY";
      case BINARY_OP_LIST_CONS:
	  return "BINARY_OP_LIST_CONS";
      default:
	  return "unknown";
      }
}

EXPR_P new_expr_leaf_int(INTVAL_P val)	/* for both PINT and NINT */
{
    EXPR_P expr = _new_expr_leaf();
    expr->u.leaf.type = LEAF_INT;
    expr->u.leaf.val.int_val = val;
    return expr;
}

EXPR_P new_expr_leaf_float(float val)
{
    EXPR_P expr = _new_expr_leaf();
    expr->u.leaf.type = LEAF_FLOAT;
    expr->u.leaf.val.float_val = val;
    return expr;
}

EXPR_P new_expr_leaf_string(char *val)
{
    EXPR_P expr = _new_expr_leaf();
    expr->u.leaf.type = LEAF_STRING;
    expr->u.leaf.val.string_val = val;
    return expr;
}

EXPR_P new_expr_leaf_bool(bool val)
{
    EXPR_P expr = _new_expr_leaf();
    expr->u.leaf.type = LEAF_BOOL;
    expr->u.leaf.val.bool_val = val;
    return expr;
}

EXPR_P new_expr_leaf_id(char *id)	/* id is a symname: including lexically TOK_ID and TOK_DOLLARID */
{
    EXPR_P expr = _new_expr_leaf();
    expr->u.leaf.type = LEAF_ID;
    expr->u.leaf.val.id_val = id;
    return expr;
}

//NULL for empty list, list of exprs
EXPR_P new_expr_leaf_list(LLIST_P list)
{
    EXPR_P expr = _new_expr_leaf();
    expr->u.leaf.type = LEAF_LIST;
    expr->u.leaf.val.list_val = list;
    return expr;
}

EXPR_P new_expr_binary(BINARY_OP_TYPE op, EXPR_P lhs, EXPR_P rhs)
{
    EXPR_P expr = _new_expr(EXPR_BINARY);
    expr->u.binary_op.op = op;
    expr->lhs = lhs;
    expr->rhs = rhs;

    return expr;
}

EXPR_P new_expr_assignment(EXPR_P lhs, EXPR_P rhs)
{
    EXPR_P expr = _new_expr(EXPR_ASSIGNMENT);
    expr->lhs = lhs;
    expr->rhs = rhs;

    return expr;
}


//pass argslist=NULL foremptylist
EXPR_P new_expr_functioncall(char *funcname, LLIST_P args_expr_list)
{
    EXPR_P expr = _new_expr(EXPR_FUNCTIONCALL);
    expr->u.functioncall.funcname = funcname;
    expr->u.functioncall.args_expr_list = args_expr_list;
    expr->lhs = NULL;
    expr->rhs = NULL;

    return expr;

}

EXPR_P _new_expr(EXPR_TYPE type)
{
    EXPR_P expr = checked_calloc(1, sizeof(struct st_expr));	//zeroes out
    expr->type = type;
    expr->lhs = NULL;		//not really neededsince calloc
    expr->rhs = NULL;
    return expr;
}

EXPR_P _new_expr_leaf(void)
{
    EXPR_P expr = _new_expr(EXPR_LEAF);
    //already zeroed out
    return expr;
}

//takes LLIST of EXPRs
char *_leaf_list_to_str(LLIST_P list)
{
    LLIST_P walker;
    char *ret = checked_malloc(5000);

    ret[0] = '[';
    ret[1] = '\0';
    for (walker = list; walker != NULL; walker = walker->next)
      {
	  EXPR_P expr = walker->element;
	  if (expr->type == EXPR_LEAF)
	    {
		struct st_leaf *list_element = &(expr->u.leaf);

		char *str = _st_leaf_val_to_str(list_element);
		strcat(ret, str);
		//TODO: free is corrupting results..
		// free(str);
	    }
	  //else was BINARY OP or FUNC CALL,
	  else
	      strcat(ret, "<expression>");	//NOTE: wont happen. but may in future.

	  if (walker->next != NULL)
	      strcat(ret, ", ");
      }
    strcat(ret, "]");
    return ret;
}
